//
//  HKLabel.swift
//  
//
//  Created by xp on 2023/4/21.
//

import UIKit
import CoreText



open class HKCustomLabel: UILabel {
    
    
    public var responseRange: [NSRange] = []
    
    public var responseBlock: ((String) -> Void)?
    
    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let point = touches.first?.preciseLocation(in: self) else {
            return
        }
        
        let idx = getIndex(point)
        if let idx = idx {
            for range in responseRange {
                if range.contains(idx) {
                    let str = (attributedText?.string ?? "") as NSString
                    responseBlock?(str.substring(with: range))
                    break
                }
            }
        }
    }
 
    private func getIndex(_ point: CGPoint) -> Int? {
        
        guard let attributedText = attributedText else {
            return nil
        }
        
        let framesetter = CTFramesetterCreateWithAttributedString(attributedText)
        let path = CGMutablePath()
        path.addRect(.init(origin: .zero, size: .init(width: bounds.width, height: bounds.height+20)))
        
        let frame = CTFramesetterCreateFrame(framesetter, .init(location: 0, length: attributedText.length), path, nil)
        let lines = CTFrameGetLines(frame)
        let numberLines = CFArrayGetCount(lines)
        var origins = [CGPoint](repeating: .zero, count: numberLines)
        CTFrameGetLineOrigins(frame, .init(location: 0, length: attributedText.length), &origins)
        
        var offset_y: CGFloat = 0.0
        
        for i in 0..<numberLines {
            
            let line = CFArrayGetValueAtIndex(lines, i)
            let lineRef = unsafeBitCast(line, to: CTLine.self)
            var ascent: CGFloat = 0.0, descent: CGFloat = 0.0, leading: CGFloat = 0.0
            let w = CGFloat(CTLineGetTypographicBounds(lineRef, &ascent, &descent, &leading))
            
            let paragraphStyle = attributedText.attribute(NSAttributedString.Key.paragraphStyle, at: 0, effectiveRange: nil)
            let style = paragraphStyle as? NSParagraphStyle
            
            var lineSpace: CGFloat = 0.0
            
            if style != nil {
                lineSpace = style!.lineSpacing
            }
            
            let lineH = ascent + abs(descent) + leading
            
            let lineY = offset_y + lineSpace * CGFloat(i)
            
            defer {
                offset_y += lineH + lineSpace * CGFloat(i)
            }
            
            if !(point.y >= lineY && point.y <= lineY + lineH) {
                continue
            }
            
            func left() -> CFIndex? {
                if point.x <= w {
                    let idx = CTLineGetStringIndexForPosition(lineRef, point)
                    return idx
                }
                return nil
            }
            
            func center() -> CFIndex? {
                let spaceWidth = (bounds.width - w)/2
                if point.x >= spaceWidth ,point.x - spaceWidth <= w {
                    let point: CGPoint = .init(x: point.x - spaceWidth, y: point.y)
                    let idx = CTLineGetStringIndexForPosition(lineRef, point)
                    return idx
                }
                return nil
            }
            
            func right() -> CFIndex? {
                let leftSpace = bounds.width - w
                if point.x >= leftSpace {
                    let point: CGPoint = .init(x: point.x - leftSpace, y: point.y)
                    let idx = CTLineGetStringIndexForPosition(lineRef, point)
                    return idx
                }
                return nil
            }
            
            switch style?.alignment {
            case .left:
                return left()
            case .center:
                return center()
            case .right:
                return right()
            case .natural:
                switch semanticContentAttribute {
                case .forceLeftToRight:
                    return left()
                case .forceRightToLeft:
                    return right()
                case .unspecified:
                    if UIWindow.current?.semanticContentAttribute == .forceRightToLeft {
                        return right()
                    }
                    return left()
                default:
                    return left()
                }
            default:
                return left()
            }
        }
        
        return nil
        
    }
}
